home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / bezrkdos.c < prev    next >
C/C++ Source or Header  |  1996-03-14  |  48KB  |  1,572 lines

  1. /*
  2.  * Program:    Bezrkdos mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    5 July 1994
  13.  * Last Edited:    14 March 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39. #include <errno.h>
  40. #include <fcntl.h>
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <time.h>
  44. #include <sys/stat.h>
  45. #include <dos.h>
  46. #include <io.h>
  47. #include "bezrkdos.h"
  48. #include "rfc822.h"
  49. #include "misc.h"
  50. #include "dummy.h"
  51.  
  52. /* Bezrkdos mail routines */
  53.  
  54.  
  55. /* Driver dispatch used by MAIL */
  56.  
  57. DRIVER bezrkdosdriver = {
  58.   "bezrkdos",            /* driver name */
  59.   (DRIVER *) NIL,        /* next driver */
  60.   bezrkdos_valid,        /* mailbox is valid for us */
  61.   bezrkdos_parameters,        /* manipulate parameters */
  62.   bezrkdos_find,        /* find mailboxes */
  63.   bezrkdos_find_bboards,    /* find bboards */
  64.   bezrkdos_find_all,        /* find all mailboxes */
  65.   bezrkdos_find_bboards,    /* find all bboards */
  66.   bezrkdos_subscribe,        /* subscribe to mailbox */
  67.   bezrkdos_unsubscribe,        /* unsubscribe from mailbox */
  68.   bezrkdos_subscribe_bboard,    /* subscribe to bboard */
  69.   bezrkdos_subscribe_bboard,    /* unsubscribe (same as subscribe) */
  70.   bezrkdos_create,        /* create mailbox */
  71.   bezrkdos_delete,        /* delete mailbox */
  72.   bezrkdos_rename,        /* rename mailbox */
  73.   bezrkdos_open,        /* open mailbox */
  74.   bezrkdos_close,        /* close mailbox */
  75.   bezrkdos_fetchfast,        /* fetch message "fast" attributes */
  76.   bezrkdos_fetchflags,        /* fetch message flags */
  77.   bezrkdos_fetchstructure,    /* fetch message envelopes */
  78.   bezrkdos_fetchheader,        /* fetch message header only */
  79.   bezrkdos_fetchtext,        /* fetch message body only */
  80.   bezrkdos_fetchbody,        /* fetch message body section */
  81.   bezrkdos_setflag,        /* set message flag */
  82.   bezrkdos_clearflag,        /* clear message flag */
  83.   bezrkdos_search,        /* search for message based on criteria */
  84.   bezrkdos_ping,        /* ping mailbox to see if still alive */
  85.   bezrkdos_check,        /* check for new messages */
  86.   bezrkdos_expunge,        /* expunge deleted messages */
  87.   bezrkdos_copy,        /* copy messages to another mailbox */
  88.   bezrkdos_move,        /* move messages to another mailbox */
  89.   bezrkdos_append,        /* append string message to mailbox */
  90.   bezrkdos_gc            /* garbage collect stream */
  91. };
  92.  
  93.                 /* prototype stream */
  94. MAILSTREAM bezrkdosproto = {&bezrkdosdriver};
  95.  
  96. /* Bezrkdos mail validate mailbox
  97.  * Accepts: mailbox name
  98.  * Returns: our driver if name is valid, NIL otherwise
  99.  */
  100.  
  101. DRIVER *bezrkdos_valid (char *name)
  102. {
  103.   return bezrkdos_isvalid (name) ? &bezrkdosdriver : (DRIVER *) NIL;
  104. }
  105.  
  106.  
  107. /* Bezrkdos mail test for valid mailbox
  108.  * Accepts: mailbox name
  109.  * Returns: T if valid, NIL otherwise
  110.  */
  111.  
  112. int bezrkdos_isvalid (char *name)
  113. {
  114.   int fd;
  115.   int ret = NIL;
  116.   char tmp[MAILTMPLEN];
  117.   struct stat sbuf;
  118.   errno = EINVAL;        /* assume invalid argument */
  119.                 /* if file, get its status */
  120.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  121.       mailboxfile (tmp,name) && !stat (tmp,&sbuf)) {
  122.     if (!sbuf.st_size)errno = 0;/* empty file */
  123.     else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) {
  124.       memset (tmp,'\0',MAILTMPLEN);
  125.       errno = -1;        /* in case bezrkdos_valid_line fails */
  126.       if (read (fd,tmp,MAILTMPLEN-1) >= 0)
  127.     ret = bezrkdos_valid_line (tmp,NIL,NIL);
  128.       close (fd);        /* close the file */
  129.     }
  130.   }
  131.   return ret;            /* failed miserably */
  132. }
  133.  
  134. /* Validate line
  135.  * Accepts: pointer to candidate string to validate as a From header
  136.  *        return pointer to end of date/time field
  137.  *        return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
  138.  *        return pointer to offset from t of time zone (if non-zero)
  139.  * Returns: t,ti,zn set if valid From string, else ti is NIL
  140.  */
  141.  
  142. int bezrkdos_valid_line (char *s,char **rx,int *rzn)
  143. {
  144.   char *x;
  145.   int zn;
  146.   int ti = 0;
  147.                 /* line must begin with "From " */
  148.   if ((*s != 'F') || (s[1] != 'r') || (s[2] != 'o') || (s[3] != 'm') ||
  149.     (s[4] != ' ')) return NIL;
  150.                 /* find end of line */
  151.   for (x = s + 5; *x && *x != '\012'; x++);
  152.   if (!x) return NIL;        /* end of line not found */
  153.   if (x[-1] == '\015') x--;    /* ignore CR */
  154.   if ((x - s < 27)) return NIL;    /* line too short */
  155.   if (x - s >= 41) {        /* possible search for " remote from " */
  156.     for (zn = -1; x[zn] != ' '; zn--);
  157.     if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') &&
  158.     (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') &&
  159.     (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') &&
  160.     (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))
  161.       x += zn - 12;
  162.   }
  163.   if (x[-5] == ' ') {        /* ends with year? */
  164.                 /* no timezone? */
  165.     if (x[-8] == ':') zn = 0,ti = -5;
  166.                 /* three letter timezone? */
  167.     else if (x[-9] == ' ') ti = zn = -9;
  168.                 /* numeric timezone? */
  169.     else if ((x[-11]==' ') && ((x[-10]=='+') || (x[-10]=='-'))) ti = zn = -11;
  170.   }
  171.   else if (x[-4] == ' ') {    /* no year and three leter timezone? */
  172.     if (x[-9] == ' ') zn = -4,ti = -9;
  173.   }
  174.   else if (x[-6] == ' ') {    /* no year and numeric timezone? */
  175.     if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-')))
  176.       zn = -6,ti = -11;
  177.   }
  178.                 /* time must be www mmm dd hh:mm[:ss] */
  179.   if (ti && !((x[ti - 3] == ':') &&
  180.           (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') &&
  181.           (x[ti - 3] == ' ') && (x[ti - 7] == ' ') &&
  182.           (x[ti - 11] == ' '))) return NIL;
  183.   if (rx) *rx = x;        /* set return values */
  184.   if (rzn) *rzn = zn;
  185.   return ti;
  186. }
  187.  
  188. /* Bezrkdos manipulate driver parameters
  189.  * Accepts: function code
  190.  *        function-dependent value
  191.  * Returns: function-dependent return value
  192.  */
  193.  
  194. void *bezrkdos_parameters (long function,void *value)
  195. {
  196.   return NIL;
  197. }
  198.  
  199. /* Bezrkdos mail find list of mailboxes
  200.  * Accepts: mail stream
  201.  *        pattern to search
  202.  */
  203.  
  204. void bezrkdos_find (MAILSTREAM *stream,char *pat)
  205. {
  206.   if (stream) dummy_find (NIL,pat);
  207. }
  208.  
  209.  
  210. /* Bezrkdos mail find list of bboards
  211.  * Accepts: mail stream
  212.  *        pattern to search
  213.  */
  214.  
  215. void bezrkdos_find_bboards (MAILSTREAM *stream,char *pat)
  216. {
  217.   /* always a no-op */
  218. }
  219.  
  220.  
  221. /* Bezrkdos mail find list of all mailboxes
  222.  * Accepts: mail stream
  223.  *        pattern to search
  224.  */
  225.  
  226. void bezrkdos_find_all (MAILSTREAM *stream,char *pat)
  227. {
  228.   if (stream) dummy_find_all (NIL,pat);
  229. }
  230.  
  231. /* Bezrkdos mail subscribe to mailbox
  232.  * Accepts: mail stream
  233.  *        mailbox to add to subscription list
  234.  * Returns: T on success, NIL on failure
  235.  */
  236.  
  237. long bezrkdos_subscribe (MAILSTREAM *stream,char *mailbox)
  238. {
  239.   return dummy_subscribe (stream,mailbox);
  240. }
  241.  
  242.  
  243. /* Bezrkdos mail unsubscribe to mailbox
  244.  * Accepts: mail stream
  245.  *        mailbox to delete from subscription list
  246.  * Returns: T on success, NIL on failure
  247.  */
  248.  
  249. long bezrkdos_unsubscribe (MAILSTREAM *stream,char *mailbox)
  250. {
  251.   return dummy_unsubscribe (stream,mailbox);
  252. }
  253.  
  254.  
  255. /* Bezrkdos mail subscribe to bboard
  256.  * Accepts: mail stream
  257.  *        bboard to add to subscription list
  258.  * Returns: T on success, NIL on failure
  259.  */
  260.  
  261. long bezrkdos_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  262. {
  263.   return NIL;            /* never valid for Bezrkdos */
  264. }
  265.  
  266. /* Bezrkdos mail create mailbox
  267.  * Accepts: MAIL stream
  268.  *        mailbox name to create
  269.  * Returns: T on success, NIL on failure
  270.  */
  271.  
  272. long bezrkdos_create (MAILSTREAM *stream,char *mailbox)
  273. {
  274.   return dummy_create (stream,mailbox);
  275. }
  276.  
  277.  
  278. /* Bezrkdos mail delete mailbox
  279.  * Accepts: MAIL stream
  280.  *        mailbox name to delete
  281.  * Returns: T on success, NIL on failure
  282.  */
  283.  
  284. long bezrkdos_delete (MAILSTREAM *stream,char *mailbox)
  285. {
  286.   return dummy_delete (stream,mailbox);
  287. }
  288.  
  289.  
  290. /* Bezrkdos mail rename mailbox
  291.  * Accepts: MAIL stream
  292.  *        old mailbox name
  293.  *        new mailbox name (or NIL for delete)
  294.  * Returns: T on success, NIL on failure
  295.  */
  296.  
  297. long bezrkdos_rename (MAILSTREAM *stream,char *old,char *new)
  298. {
  299.   return dummy_rename (stream,old,new);
  300. }
  301.  
  302. /* Bezrkdos mail open
  303.  * Accepts: stream to open
  304.  * Returns: stream on success, NIL on failure
  305.  */
  306.  
  307. MAILSTREAM *bezrkdos_open (MAILSTREAM *stream)
  308. {
  309.   long i;
  310.   int fd;
  311.   char *s;
  312.   char tmp[MAILTMPLEN];
  313.   struct stat sbuf;
  314.                 /* return prototype for OP_PROTOTYPE call */
  315.   if (!stream) return &bezrkdosproto;
  316.   if (LOCAL) {            /* close old file if stream being recycled */
  317.     bezrkdos_close (stream);    /* dump and save the changes */
  318.     stream->dtb = &bezrkdosdriver;    /* reattach this driver */
  319.     mail_free_cache (stream);    /* clean up cache */
  320.   }
  321.   else {            /* flush flagstring and flags if any */
  322.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  323.     for (i = 0; i < NUSERFLAGS; ++i) stream->user_flags[i] = NIL;
  324.   }
  325.   if (!mailboxfile (tmp,stream->mailbox))
  326.     return (MAILSTREAM *) bezrkdos_badname (tmp,stream->mailbox);
  327.   if (((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0)) {
  328.     sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  329.     mm_log (tmp,ERROR);
  330.     return NIL;
  331.   }
  332.   stream->rdonly = T;        /* this driver is readonly */
  333.   stream->local = fs_get (sizeof (BEZRKDOSLOCAL));
  334.                 /* canonicalize the stream mailbox name */
  335.   fs_give ((void **) &stream->mailbox);
  336.   if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0';
  337.   stream->mailbox = cpystr (tmp);
  338.   LOCAL->fd = fd;        /* note the file */
  339.   LOCAL->filesize = 0;        /* initialize parsed file size */
  340.   stream->sequence++;        /* bump sequence number */
  341.                 /* parse mailbox */
  342.   stream->nmsgs = stream->recent = 0;
  343.   if (!bezrkdos_ping (stream)) return NIL;
  344.   if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL);
  345.   return stream;        /* return stream to caller */
  346. }
  347.  
  348. /* Bezrkdos mail close
  349.  * Accepts: MAIL stream
  350.  */
  351.  
  352. void bezrkdos_close (MAILSTREAM *stream)
  353. {
  354.   long i;
  355.   if (stream && LOCAL) {    /* only if a file is open */
  356.     close (LOCAL->fd);        /* close the local file */
  357.                 /* nuke the local data */
  358.     fs_give ((void **) &stream->local);
  359.     stream->dtb = NIL;        /* log out the DTB */
  360.   }
  361. }
  362.  
  363.  
  364. /* Bezrkdos mail fetch fast information
  365.  * Accepts: MAIL stream
  366.  *        sequence
  367.  */
  368.  
  369. void bezrkdos_fetchfast (MAILSTREAM *stream,char *sequence)
  370. {
  371.   return;            /* no-op for local mail */
  372. }
  373.  
  374.  
  375. /* Bezrkdos mail fetch flags
  376.  * Accepts: MAIL stream
  377.  *        sequence
  378.  */
  379.  
  380. void bezrkdos_fetchflags (MAILSTREAM *stream,char *sequence)
  381. {
  382.   return;            /* no-op for local mail */
  383. }
  384.  
  385. /* Bezrkdos string driver for file stringstructs */
  386.  
  387. STRINGDRIVER bezrkdos_string = {
  388.   bezrkdos_string_init,        /* initialize string structure */
  389.   bezrkdos_string_next,        /* get next byte in string structure */
  390.   bezrkdos_string_setpos    /* set position in string structure */
  391. };
  392.  
  393.  
  394. /* Cache buffer for file stringstructs */
  395.  
  396. #define DOSCHUNKLEN 4096
  397. char dos_chunk[DOSCHUNKLEN];
  398.  
  399.  
  400. /* Initialize bezrkdos string structure for file stringstruct
  401.  * Accepts: string structure
  402.  *        pointer to string
  403.  *        size of string
  404.  */
  405.  
  406. void bezrkdos_string_init (STRING *s,void *data,unsigned long size)
  407. {
  408.   BEZRKDOSDATA *d = (BEZRKDOSDATA *) data;
  409.   s->data = (void *) d->fd;    /* note fd */
  410.   s->data1 = d->pos;        /* note file offset */
  411.   s->size = size;        /* note size */
  412.   s->curpos = s->chunk = dos_chunk;
  413.   s->chunksize = (unsigned long) DOSCHUNKLEN;
  414.   s->offset = 0;        /* initial position */
  415.                 /* and size of data */
  416.   s->cursize = min (s->chunksize,size);
  417.                 /* move to that position in the file */
  418.   lseek (d->fd,d->pos,SEEK_SET);
  419.   read (d->fd,s->chunk,(unsigned int) s->cursize);
  420. }
  421.  
  422. /* Get next character from file stringstruct
  423.  * Accepts: string structure
  424.  * Returns: character, string structure chunk refreshed
  425.  */
  426.  
  427. char bezrkdos_string_next (STRING *s)
  428. {
  429.   char c = *s->curpos++;    /* get next byte */
  430.                 /* move to next chunk */
  431.   SETPOS (s,s->offset + s->chunksize);
  432.   return c;            /* return the byte */
  433. }
  434.  
  435.  
  436. /* Set string pointer position for file stringstruct
  437.  * Accepts: string structure
  438.  *        new position
  439.  */
  440.  
  441. void bezrkdos_string_setpos (STRING *s,unsigned long i)
  442. {
  443.   s->offset = i;        /* set new offset */
  444.   s->curpos = s->chunk;        /* reset position */
  445.                 /* set size of data */
  446.   if (s->cursize = s->size > s->offset ? min (s->chunksize,SIZE (s)) : 0) {
  447.                 /* move to that position in the file */
  448.     lseek ((int) s->data,s->data1 + s->offset,SEEK_SET);
  449.     read ((int) s->data,s->curpos,(unsigned int) s->cursize);
  450.   }
  451. }
  452.  
  453. /* Bezrkdos mail fetch structure
  454.  * Accepts: MAIL stream
  455.  *        message # to fetch
  456.  *        pointer to return body
  457.  * Returns: envelope of this message, body returned in body value
  458.  *
  459.  * Fetches the "fast" information as well
  460.  */
  461.  
  462. #define MAXHDR (unsigned long) 4*MAILTMPLEN
  463.  
  464. ENVELOPE *bezrkdos_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  465. {
  466.   LONGCACHE *lelt;
  467.   ENVELOPE **env;
  468.   BODY **b;
  469.   STRING bs;
  470.   BEZRKDOSDATA d;
  471.   unsigned long hdrsize;
  472.   unsigned long hdrpos = bezrkdos_header (stream,msgno,&hdrsize);
  473.   unsigned long textsize = body ? bezrkdos_size (stream,msgno) - hdrsize : 0;
  474.                 /* limit header size */
  475.   if (hdrsize > MAXHDR) hdrsize = MAXHDR;
  476.   if (stream->scache) {        /* short cache */
  477.     if (msgno != stream->msgno){/* flush old poop if a different message */
  478.       mail_free_envelope (&stream->env);
  479.       mail_free_body (&stream->body);
  480.     }
  481.     stream->msgno = msgno;
  482.     env = &stream->env;        /* get pointers to envelope and body */
  483.     b = &stream->body;
  484.   }
  485.   else {            /* long cache */
  486.     lelt = mail_lelt (stream,msgno);
  487.     env = &lelt->env;        /* get pointers to envelope and body */
  488.     b = &lelt->body;
  489.   }
  490.  
  491.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  492.     char *hdr = (char *) fs_get (hdrsize + 1);
  493.     char *tmp = (char *) fs_get (MAXHDR);
  494.     mail_free_envelope (env);    /* flush old envelope and body */
  495.     mail_free_body (b);
  496.                 /* get to header position */
  497.     lseek (LOCAL->fd,hdrpos,SEEK_SET);
  498.                 /* read the text */
  499.     if (read (LOCAL->fd,hdr,(unsigned int) hdrsize) >= 0) {
  500.       if (hdr[hdrsize-1] != '\012') hdr[hdrsize-1] = '\012';
  501.       hdr[hdrsize] = '\0';    /* make sure tied off */
  502.       d.fd = LOCAL->fd;        /* set initial stringstruct */
  503.       d.pos = hdrpos + hdrsize;
  504.       INIT (&bs,bezrkdos_string,(void *) &d,textsize);
  505.                 /* parse envelope and body */
  506.       rfc822_parse_msg (env,body ? b : NIL,hdr,hdrsize,&bs,mylocalhost (),tmp);
  507.     }
  508.     fs_give ((void **) &tmp);
  509.     fs_give ((void **) &hdr);
  510.   }
  511.   if (body) *body = *b;        /* return the body */
  512.   return *env;            /* return the envelope */
  513. }
  514.  
  515. /* Bezrkdos mail fetch message header
  516.  * Accepts: MAIL stream
  517.  *        message # to fetch
  518.  * Returns: message header in RFC822 format
  519.  */
  520.  
  521. char *bezrkdos_fetchheader (MAILSTREAM *stream,long msgno)
  522. {
  523.   unsigned long hdrsize;
  524.   unsigned long hdrpos = bezrkdos_header (stream,msgno,&hdrsize);
  525.   if (stream->text) fs_give ((void **) &stream->text);
  526.   return stream->text = bezrkdos_slurp (stream,hdrpos,&hdrsize);
  527. }
  528.  
  529.  
  530. /* Bezrkdos mail fetch message text (body only)
  531.  * Accepts: MAIL stream
  532.  *        message # to fetch
  533.  * Returns: message text in RFC822 format
  534.  */
  535.  
  536. char *bezrkdos_fetchtext (MAILSTREAM *stream,long msgno)
  537. {
  538.   unsigned long hdrsize;
  539.   unsigned long hdrpos = bezrkdos_header (stream,msgno,&hdrsize);
  540.   unsigned long textsize = bezrkdos_size (stream,msgno) - hdrsize;
  541.   if (stream->text) fs_give ((void **) &stream->text);
  542.                 /* mark message as seen */
  543.   mail_elt (stream,msgno)->seen = T;
  544.   return stream->text = bezrkdos_slurp (stream,hdrpos + hdrsize,&textsize);
  545. }
  546.  
  547. /* Bezrkdos fetch message body as a structure
  548.  * Accepts: Mail stream
  549.  *        message # to fetch
  550.  *        section specifier
  551.  *        pointer to length
  552.  * Returns: pointer to section of message body
  553.  */
  554.  
  555. char *bezrkdos_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  556. {
  557.   BODY *b;
  558.   PART *pt;
  559.   unsigned long i;
  560.   unsigned long base;
  561.   unsigned long offset = 0;
  562.   unsigned long hdrpos = bezrkdos_header (stream,m,&base);
  563.   MESSAGECACHE *elt = mail_elt (stream,m);
  564.   if (stream->text) fs_give ((void **) &stream->text);
  565.                 /* make sure have a body */
  566.   if (!(bezrkdos_fetchstructure (stream,m,&b) && b && s && *s &&
  567.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  568.   do {                /* until find desired body part */
  569.                 /* multipart content? */
  570.     if (b->type == TYPEMULTIPART) {
  571.       pt = b->contents.part;    /* yes, find desired part */
  572.       while (--i && (pt = pt->next));
  573.       if (!pt) return NIL;    /* bad specifier */
  574.                 /* note new body, check valid nesting */
  575.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  576.       offset = pt->offset;    /* get new offset */
  577.     }
  578.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  579.                 /* need to go down further? */
  580.     if (i = *s) switch (b->type) {
  581.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  582.       offset = b->contents.msg.offset;
  583.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  584.     case TYPEMULTIPART:        /* multipart, get next section */
  585.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  586.     default:            /* bogus subpart specification */
  587.       return NIL;
  588.     }
  589.   } while (i);
  590.                 /* lose if body bogus */
  591.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  592.   elt->seen = T;        /* mark message as seen */
  593.   *len = b->size.bytes;        /* number of bytes from file */
  594.   return stream->text = bezrkdos_slurp (stream,hdrpos + base + offset,len);
  595. }
  596.  
  597. /* Bezrkdos mail slurp
  598.  * Accepts: MAIL stream
  599.  *        file position
  600.  *        pointer to number of file bytes to read
  601.  * Returns: buffer address, actual number of bytes written
  602.  */
  603.  
  604. char *bezrkdos_slurp (MAILSTREAM *stream,unsigned long pos,
  605.               unsigned long *count)
  606. {
  607.   unsigned long cnt = *count;
  608.   int i,j;
  609.   char tmp[MAILTMPLEN];
  610.   mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
  611.   lseek(LOCAL->fd,pos,SEEK_SET);/* get to desired position */
  612.   LOCAL->ch = '\0';        /* initialize CR mechanism */
  613.   while (cnt) {            /* until checked all bytes */
  614.                 /* number of bytes this chunk */
  615.     cnt -= (i = (int) min (cnt,(unsigned long) MAILTMPLEN));
  616.                 /* read a chunk */
  617.     if (read (LOCAL->fd,tmp,i) != i) return NIL;
  618.     for (j = 0; j < i; j++) {    /* count bytes in chunk */
  619.                 /* if see bare LF, count next as CR */
  620.       if ((tmp[j] == '\012') && (LOCAL->ch != '\015')) ++*count;
  621.       LOCAL->ch = tmp[j];
  622.     }
  623.   }
  624.   lseek(LOCAL->fd,pos,SEEK_SET);/* get to desired position */
  625.   LOCAL->ch = '\0';        /* initialize CR mechanism */
  626.   return (mg ? *mg : mm_gets) (bezrkdos_read,stream,*count);
  627. }
  628.  
  629.  
  630. /* Bezrkdos mail read
  631.  * Accepts: MAIL stream
  632.  *        number of bytes to read
  633.  *        buffer address
  634.  * Returns: T if success, NIL otherwise
  635.  */
  636.  
  637. long bezrkdos_read (MAILSTREAM *stream,unsigned long count,char *buffer)
  638. {
  639.   char tmp[MAILTMPLEN];
  640.   int i,j;
  641.   while (count) {        /* until no more bytes to do */
  642.                 /* read a chunk */
  643.     if ((i = read (LOCAL->fd,tmp,(int) min (count,(unsigned long) MAILTMPLEN)))
  644.     < 0) return NIL;
  645.                 /* for each byte in chunk (or filled) */
  646.     for (j = 0; count && (j < i); count--) {
  647.                 /* if see LF, insert CR unless already there */
  648.       if ((tmp[j] == '\012') && (LOCAL->ch != '\015')) LOCAL->ch = '\015';
  649.       else LOCAL->ch = tmp[j++];/* regular character */
  650.       *buffer++ = LOCAL->ch;    /* poop in buffer */
  651.     }
  652.     if (i -= j) lseek (LOCAL->fd,-((long) i),SEEK_CUR);
  653.   }
  654.   return T;
  655. }
  656.  
  657. /* Bezrkdos locate header for a message
  658.  * Accepts: MAIL stream
  659.  *        message number
  660.  *        pointer to returned header size
  661.  * Returns: position of header in file
  662.  */
  663.  
  664. unsigned long bezrkdos_header (MAILSTREAM *stream,long msgno,
  665.                    unsigned long *size)
  666. {
  667.   long siz;
  668.   long i = 0;
  669.   char c = '\0';
  670.   char *s;
  671.   char tmp[MAILTMPLEN];
  672.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  673.   long pos = elt->data1 + (elt->data2 >> 24);
  674.   long msiz = bezrkdos_size (stream,msgno);
  675.                 /* is size known? */
  676.   if (!(*size = (elt->data2 & (unsigned long) 0xffffff))) {
  677.                 /* get to header position */
  678.     lseek (LOCAL->fd,pos,SEEK_SET);
  679.                 /* search message for CRLF CRLF */
  680.     for (siz = 0; siz < msiz; siz++) {
  681.                 /* read another buffer as necessary */
  682.       if (--i <= 0)        /* buffer empty? */
  683.     if (read (LOCAL->fd,s = tmp,
  684.           i = min (msiz-siz,(unsigned long) MAILTMPLEN)) < 0)
  685.       return pos;        /* I/O error? */
  686.                 /* two newline sequence? */
  687.       if ((c == '\012') && (*s == '\012')) {
  688.                 /* yes, note for later */
  689.     elt->data2 |= (*size = siz + 1);
  690.     return pos;        /* return to caller */
  691.       }
  692.       else if ((c == '\012') && (*s == '\015')) {
  693.                 /* yes, note for later */
  694.     elt->data2 |= (*size = siz + 2);
  695.     return pos;        /* return to caller */
  696.       }
  697.       else c = *s++;        /* next character */
  698.  
  699.     }
  700.   }
  701.   return pos;            /* have position */
  702. }
  703.  
  704. /* Bezrkdos mail set flag
  705.  * Accepts: MAIL stream
  706.  *        sequence
  707.  *        flag(s)
  708.  */
  709.  
  710. void bezrkdos_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  711. {
  712.   MESSAGECACHE *elt;
  713.   long i;
  714.   short f = bezrkdos_getflags (stream,flag);
  715.   if (!f) return;        /* no-op if no flags to modify */
  716.                 /* get sequence and loop on it */
  717.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  718.     if ((elt = mail_elt (stream,i))->sequence) {
  719.                 /* set all requested flags */
  720.       if (f&fSEEN) elt->seen = T;
  721.       if (f&fDELETED) elt->deleted = T;
  722.       if (f&fFLAGGED) elt->flagged = T;
  723.       if (f&fANSWERED) elt->answered = T;
  724.     }
  725. }
  726.  
  727. /* Bezrkdos mail clear flag
  728.  * Accepts: MAIL stream
  729.  *        sequence
  730.  *        flag(s)
  731.  */
  732.  
  733. void bezrkdos_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  734. {
  735.   MESSAGECACHE *elt;
  736.   long i;
  737.   short f = bezrkdos_getflags (stream,flag);
  738.   if (!f) return;        /* no-op if no flags to modify */
  739.                 /* get sequence and loop on it */
  740.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  741.     if ((elt = mail_elt (stream,i))->sequence) {
  742.                 /* clear all requested flags */
  743.       if (f&fSEEN) elt->seen = NIL;
  744.       if (f&fDELETED) elt->deleted = NIL;
  745.       if (f&fFLAGGED) elt->flagged = NIL;
  746.       if (f&fANSWERED) elt->answered = NIL;
  747.     }
  748. }
  749.  
  750. /* Bezrkdos mail search for messages
  751.  * Accepts: MAIL stream
  752.  *        search criteria
  753.  */
  754.  
  755. void bezrkdos_search (MAILSTREAM *stream,char *criteria)
  756. {
  757.   long i,n;
  758.   char *d;
  759.   search_t f;
  760.                 /* initially all searched */
  761.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  762.                 /* get first criterion */
  763.   if (criteria && (criteria = strtok (criteria," "))) {
  764.                 /* for each criterion */
  765.     for (; criteria; (criteria = strtok (NIL," "))) {
  766.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  767.       switch (*ucase (criteria)) {
  768.       case 'A':            /* possible ALL, ANSWERED */
  769.     if (!strcmp (criteria+1,"LL")) f = bezrkdos_search_all;
  770.     else if (!strcmp (criteria+1,"NSWERED")) f = bezrkdos_search_answered;
  771.     break;
  772.       case 'B':            /* possible BCC, BEFORE, BODY */
  773.     if (!strcmp (criteria+1,"CC"))
  774.       f = bezrkdos_search_string (bezrkdos_search_bcc,&d,&n);
  775.     else if (!strcmp (criteria+1,"EFORE"))
  776.       f = bezrkdos_search_date (bezrkdos_search_before,&n);
  777.     else if (!strcmp (criteria+1,"ODY"))
  778.       f = bezrkdos_search_string (bezrkdos_search_body,&d,&n);
  779.     break;
  780.       case 'C':            /* possible CC */
  781.     if (!strcmp (criteria+1,"C")) 
  782.       f = bezrkdos_search_string (bezrkdos_search_cc,&d,&n);
  783.     break;
  784.       case 'D':            /* possible DELETED */
  785.     if (!strcmp (criteria+1,"ELETED")) f = bezrkdos_search_deleted;
  786.     break;
  787.       case 'F':            /* possible FLAGGED, FROM */
  788.     if (!strcmp (criteria+1,"LAGGED")) f = bezrkdos_search_flagged;
  789.     else if (!strcmp (criteria+1,"ROM"))
  790.       f = bezrkdos_search_string (bezrkdos_search_from,&d,&n);
  791.     break;
  792.       case 'K':            /* possible KEYWORD */
  793.     if (!strcmp (criteria+1,"EYWORD"))
  794.       f = bezrkdos_search_flag (bezrkdos_search_keyword,&n,stream);
  795.     break;
  796.       case 'N':            /* possible NEW */
  797.     if (!strcmp (criteria+1,"EW")) f = bezrkdos_search_new;
  798.     break;
  799.  
  800.       case 'O':            /* possible OLD, ON */
  801.     if (!strcmp (criteria+1,"LD")) f = bezrkdos_search_old;
  802.     else if (!strcmp (criteria+1,"N"))
  803.       f = bezrkdos_search_date (bezrkdos_search_on,&n);
  804.     break;
  805.       case 'R':            /* possible RECENT */
  806.     if (!strcmp (criteria+1,"ECENT")) f = bezrkdos_search_recent;
  807.     break;
  808.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  809.     if (!strcmp (criteria+1,"EEN")) f = bezrkdos_search_seen;
  810.     else if (!strcmp (criteria+1,"INCE"))
  811.       f = bezrkdos_search_date (bezrkdos_search_since,&n);
  812.     else if (!strcmp (criteria+1,"UBJECT"))
  813.       f = bezrkdos_search_string (bezrkdos_search_subject,&d,&n);
  814.     break;
  815.       case 'T':            /* possible TEXT, TO */
  816.     if (!strcmp (criteria+1,"EXT"))
  817.       f = bezrkdos_search_string (bezrkdos_search_text,&d,&n);
  818.     else if (!strcmp (criteria+1,"O"))
  819.       f = bezrkdos_search_string (bezrkdos_search_to,&d,&n);
  820.     break;
  821.       case 'U':            /* possible UN* */
  822.     if (criteria[1] == 'N') {
  823.       if (!strcmp (criteria+2,"ANSWERED")) f = bezrkdos_search_unanswered;
  824.       else if (!strcmp (criteria+2,"DELETED"))
  825.         f = bezrkdos_search_undeleted;
  826.       else if (!strcmp (criteria+2,"FLAGGED"))
  827.         f = bezrkdos_search_unflagged;
  828.       else if (!strcmp (criteria+2,"KEYWORD"))
  829.         f = bezrkdos_search_flag (bezrkdos_search_unkeyword,&n,stream);
  830.       else if (!strcmp (criteria+2,"SEEN")) f = bezrkdos_search_unseen;
  831.     }
  832.     break;
  833.       default:            /* we will barf below */
  834.     break;
  835.       }
  836.       if (!f) {            /* if can't determine any criteria */
  837.     mm_log ("Unknown search criterion",ERROR);
  838.     return;
  839.       }
  840.                 /* run the search criterion */
  841.       for (i = 1; i <= stream->nmsgs; ++i)
  842.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  843.       mail_elt (stream,i)->searched = NIL;
  844.     }
  845.                 /* report search results to main program */
  846.     for (i = 1; i <= stream->nmsgs; ++i)
  847.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  848.   }
  849. }
  850.  
  851. /* Bezrkdos mail ping mailbox
  852.  * Accepts: MAIL stream
  853.  * Returns: T if stream still alive, NIL if not
  854.  */
  855.  
  856. long bezrkdos_ping (MAILSTREAM *stream)
  857. {
  858.   long i = 0;
  859.   long r,j;
  860.   struct stat sbuf;
  861.                 /* punt if stream no longer alive */
  862.   if (!(stream && LOCAL)) return NIL;
  863.                 /* parse mailbox, punt if parse dies */
  864.   return (bezrkdos_parse (stream)) ? T : NIL;
  865. }
  866.  
  867.  
  868. /* Bezrkdos mail check mailbox (reparses status too)
  869.  * Accepts: MAIL stream
  870.  */
  871.  
  872. void bezrkdos_check (MAILSTREAM *stream)
  873. {
  874.   long i = 1;
  875.   if (bezrkdos_ping (stream)) {    /* ping mailbox */
  876.                 /* get new message status */
  877.     while (i <= stream->nmsgs) mail_elt (stream,i++);
  878.     mm_log ("Check completed",(long) NIL);
  879.   }
  880. }
  881.  
  882. /* Bezrkdos mail expunge mailbox
  883.  * Accepts: MAIL stream
  884.  */
  885.  
  886. void bezrkdos_expunge (MAILSTREAM *stream)
  887. {
  888.   mm_log ("Expunge ignored on readonly mailbox",WARN);
  889. }
  890.  
  891. /* Bezrkdos mail copy message(s)
  892.  * Accepts: MAIL stream
  893.  *        sequence
  894.  *        destination mailbox
  895.  * Returns: T if success, NIL if failed
  896.  */
  897.  
  898. long bezrkdos_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  899. {
  900.                 /* copy the messages */
  901.   return (mail_sequence (stream,sequence)) ?
  902.     bezrkdos_copy_messages (stream,mailbox) : NIL;
  903. }
  904.  
  905.  
  906. /* Bezrkdos mail move message(s)
  907.  * Accepts: MAIL stream
  908.  *        sequence
  909.  *        destination mailbox
  910.  * Returns: T if success, NIL if failed
  911.  */
  912.  
  913. long bezrkdos_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  914. {
  915.   long i;
  916.   MESSAGECACHE *elt;
  917.   if (!(mail_sequence (stream,sequence) &&
  918.     bezrkdos_copy_messages (stream,mailbox))) return NIL;
  919.                 /* delete all requested messages */
  920.   for (i = 1; i <= stream->nmsgs; i++)
  921.     if ((elt = mail_elt (stream,i))->sequence) elt->deleted = T;
  922.   return T;
  923. }
  924.  
  925. /* Bezrkdos mail append message from stringstruct
  926.  * Accepts: MAIL stream
  927.  *        destination mailbox
  928.  *        stringstruct of messages to append
  929.  * Returns: T if append successful, else NIL
  930.  */
  931.  
  932. long bezrkdos_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  933.           STRING *message)
  934. {
  935.   struct stat sbuf;
  936.   int i,fd,ti;
  937.   char c,*s,*x,tmp[MAILTMPLEN];
  938.   MESSAGECACHE elt;
  939.   long j,n,ok = T;
  940.   time_t t = time (0);
  941.   long sz = SIZE (message);
  942.   long size = 0;
  943.   short f = bezrkdos_getflags (stream,flags);
  944.   if (date) {            /* want to preserve date? */
  945.                 /* yes, parse date into an elt */
  946.     if (!mail_parse_date (&elt,date)) {
  947.       sprintf (tmp,"Bad date in append: %s",date);
  948.       mm_log (tmp,ERROR);
  949.       return NIL;
  950.     }
  951.   }
  952.   tzset ();            /* initialize timezone stuff */
  953.                 /* make sure valid mailbox */
  954.   if (!bezrkdos_isvalid (mailbox) && errno) {
  955.     if (errno == ENOENT)
  956.       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
  957.          (long) NIL);
  958.     else if (mailboxfile (tmp,mailbox)) {
  959.       sprintf (tmp,"Not a Bezrkdos-format mailbox: %s",mailbox);
  960.       mm_log (tmp,ERROR);
  961.     }
  962.     else bezrkdos_badname (tmp,mailbox);
  963.     return NIL;
  964.   }
  965.                 /* open the destination */
  966.   if ((fd = open (mailboxfile (tmp,mailbox),
  967.           O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
  968.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  969.     mm_log (tmp,ERROR);
  970.     return NIL;
  971.   }
  972.  
  973.   mm_critical (stream);        /* go critical */
  974.                 /* calculate data size w/o CR's */
  975.   while (sz--) if ((c = SNX (message)) != '\015') size++;
  976.   SETPOS (message,(long) 0);    /* back to start */
  977.   fstat (fd,&sbuf);        /* get current file size */
  978.   strcpy (tmp,"From someone ");    /* start header */
  979.                 /* write the date given */
  980.   if (date) mail_cdate (tmp + strlen (tmp),&elt);
  981.   else strcat (tmp,ctime (&t));    /* otherwise write the time now */
  982.   sprintf (tmp + strlen (tmp),"Status: %sO\nX-Status: %s%s%s\n",
  983.        f&fSEEN ? "R" : "",f&fDELETED ? "D" : "",
  984.        f&fFLAGGED ? "F" : "",f&fANSWERED ? "A" : "");
  985.   i = strlen (tmp);        /* initial buffer space used */
  986.   while (ok && size--) {    /* copy text, tossing out CR's */
  987.                 /* if at start of line */
  988.     if ((c == '\n') && (size > 5)) {
  989.       n = GETPOS (message);    /* prepend a broket if needed */
  990.       if ((SNX (message) == 'F') && (SNX (message) == 'r') &&
  991.       (SNX (message) == 'o') && (SNX (message) == 'm') &&
  992.       (SNX (message) == ' ')) {
  993.     for (j = 6; (j < size) && (SNX (message) != '\n'); j++);
  994.     if (j < size) {        /* copy line */
  995.       SETPOS (message,n);    /* restore position */
  996.       x = s = (char *) fs_get (j + 1);
  997.       while (j--) if ((c = SNX (message)) != '\015') *x++ = c;
  998.       *x = '\0';        /* tie off line */
  999.       if (ti = bezrkdos_valid_line (s,NIL,NIL))
  1000.         ok = bezrkdos_append_putc (fd,tmp,&i,'>');
  1001.       fs_give ((void **) &s);
  1002.     }
  1003.       }
  1004.       SETPOS (message,n);    /* restore position as needed */
  1005.     }
  1006.                 /* copy another character */
  1007.     if ((c = SNX (message)) != '\015') ok = bezrkdos_append_putc (fd,tmp,&i,c);
  1008.   }
  1009.                 /* write trailing newline */
  1010.   if (ok) ok = bezrkdos_append_putc (fd,tmp,&i,'\n');
  1011.   if (!(ok && (ok = (write (fd,tmp,i) >= 0)))) {
  1012.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  1013.     mm_log (tmp,ERROR);
  1014.     chsize (fd,sbuf.st_size);
  1015.   }
  1016.   close (fd);            /* close the file */
  1017.   mm_nocritical (stream);    /* release critical */
  1018.   return T;            /* return success */
  1019. }
  1020.  
  1021. /* Berkeley mail append character
  1022.  * Accepts: file descriptor
  1023.  *        output buffer
  1024.  *        pointer to current size of output buffer
  1025.  *        character to append
  1026.  * Returns: T if append successful, else NIL
  1027.  */
  1028.  
  1029. long bezrkdos_append_putc (int fd,char *s,int *i,char c)
  1030. {
  1031.   s[(*i)++] = c;
  1032.   if (*i == MAILTMPLEN) {    /* dump if buffer filled */
  1033.     if (write (fd,s,*i) < 0) return NIL;
  1034.     *i = 0;            /* reset */
  1035.   }
  1036.   return T;
  1037. }
  1038.  
  1039.  
  1040. /* Bezrkdos garbage collect stream
  1041.  * Accepts: Mail stream
  1042.  *        garbage collection flags
  1043.  */
  1044.  
  1045. void bezrkdos_gc (MAILSTREAM *stream,long gcflags)
  1046. {
  1047.   /* nothing here for now */
  1048. }
  1049.  
  1050. /* Internal routines */
  1051.  
  1052.  
  1053. /* Bezrk mail return internal message size in bytes
  1054.  * Accepts: MAIL stream
  1055.  *        message #
  1056.  * Returns: internal size of message
  1057.  */
  1058.  
  1059. unsigned long bezrkdos_size (MAILSTREAM *stream,long m)
  1060. {
  1061.   unsigned long end = (m < stream->nmsgs) ?
  1062.     mail_elt (stream,m+1)->data1 : LOCAL->filesize;
  1063.   MESSAGECACHE *elt = mail_elt (stream,m);
  1064.   return end - (elt->data1 + (elt->data2 >> 24));
  1065. }
  1066.  
  1067.  
  1068. /* Return bad file name error message
  1069.  * Accepts: temporary buffer
  1070.  *        file name
  1071.  * Returns: long NIL always
  1072.  */
  1073.  
  1074. long bezrkdos_badname (char *tmp,char *s)
  1075. {
  1076.   sprintf (tmp,"Invalid mailbox name: %s",s);
  1077.   mm_log (tmp,ERROR);
  1078.   return (long) NIL;
  1079. }
  1080.  
  1081. /* Parse flag list
  1082.  * Accepts: MAIL stream
  1083.  *        flag list as a character string
  1084.  * Returns: system flags
  1085.  */
  1086.  
  1087. long bezrkdos_getflags (MAILSTREAM *stream,char *flag)
  1088. {
  1089.   char tmp[MAILTMPLEN];
  1090.   char key[MAILTMPLEN];
  1091.   char *t,*s;
  1092.   short f = 0;
  1093.   long i;
  1094.   short j;
  1095.   if (flag && *flag) {        /* no-op if no flag string */
  1096.                 /* check if a list and make sure valid */
  1097.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1098.       mm_log ("Bad flag list",ERROR);
  1099.       return NIL;
  1100.     }
  1101.                 /* copy the flag string w/o list construct */
  1102.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1103.     tmp[j] = '\0';        /* tie off tail */
  1104.  
  1105.                 /* make uppercase, find first, parse */
  1106.     if (t = strtok (ucase (tmp)," ")) do {
  1107.       i = 0;            /* no flag yet */
  1108.                 /* system flag, dispatch on first character */
  1109.       if (*t == '\\') switch (*++t) {
  1110.       case 'S':            /* possible \Seen flag */
  1111.     if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
  1112.       f |= i = fSEEN;
  1113.     break;
  1114.       case 'D':            /* possible \Deleted flag */
  1115.     if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1116.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
  1117.     break;
  1118.       case 'F':            /* possible \Flagged flag */
  1119.     if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1120.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
  1121.     break;
  1122.       case 'A':            /* possible \Answered flag */
  1123.     if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1124.         t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
  1125.       f |= i = fANSWERED;
  1126.     break;
  1127.       default:            /* unknown */
  1128.     break;
  1129.       }
  1130.       if (!i) {            /* didn't find a matching flag? */
  1131.     sprintf (key,"Unknown flag: %.80s",t);
  1132.     mm_log (key,ERROR);
  1133.       }
  1134.                 /* parse next flag */
  1135.     } while (t = strtok (NIL," "));
  1136.   }
  1137.   return f;
  1138. }
  1139.  
  1140. /* Bezrkdos mail parse mailbox
  1141.  * Accepts: MAIL stream
  1142.  * Returns: T if parse OK
  1143.  *        NIL if failure, stream aborted
  1144.  */
  1145.  
  1146. long bezrkdos_parse (MAILSTREAM *stream)
  1147. {
  1148.   struct stat sbuf;
  1149.   MESSAGECACHE *elt;
  1150.   char *s,*t,tmp[MAILTMPLEN + 1],*db,datemsg[100];
  1151.   long i;
  1152.   int j,ti,zn;
  1153.   long curpos = LOCAL->filesize;
  1154.   long nmsgs = stream->nmsgs;
  1155.   long recent = stream->recent;
  1156.   fstat (LOCAL->fd,&sbuf);    /* get status */
  1157.   if (sbuf.st_size < curpos) {    /* sanity check */
  1158.     mm_log ("Mailbox shrank!",ERROR);
  1159.     bezrkdos_close (stream);
  1160.     return NIL;
  1161.   }
  1162.   db = datemsg + strlen (strcpy (datemsg,"Unparsable date: "));
  1163.                 /* while there is data to read */
  1164.   while (i = sbuf.st_size - curpos) {
  1165.                 /* get to that position in the file */
  1166.     lseek (LOCAL->fd,curpos,SEEK_SET);
  1167.                 /* read first buffer's worth */
  1168.     read (LOCAL->fd,tmp,j = (int) min (i,(long) MAILTMPLEN));
  1169.     tmp[j] = '\0';        /* tie off buffer */
  1170.     if (!(ti = bezrkdos_valid_line (tmp,&t,&zn))) {
  1171.       mm_log ("Mailbox format invalidated (consult an expert), aborted",ERROR);
  1172.       bezrkdos_close (stream);
  1173.       return NIL;
  1174.     }
  1175.  
  1176.                 /* count up another message, make elt */
  1177.     (elt = mail_elt (stream,++nmsgs))->data1 = curpos;
  1178.     elt->valid = T;        /* mark as valid */
  1179.     elt->data2 = ((s = ((*t == '\015') ? (t + 2) : (t + 1))) - tmp) << 24;
  1180.                 /* generate plausable IMAPish date string */
  1181.     db[2] = db[6] = db[20] = '-'; db[11] = ' '; db[14] = db[17] = ':';
  1182.                 /* dd */
  1183.     db[0] = t[ti - 2]; db[1] = t[ti - 1];
  1184.                 /* mmm */
  1185.     db[3] = t[ti - 6]; db[4] = t[ti - 5]; db[5] = t[ti - 4];
  1186.                 /* hh */
  1187.     db[12] = t[ti + 1]; db[13] = t[ti + 2];
  1188.                 /* mm */
  1189.     db[15] = t[ti + 4]; db[16] = t[ti + 5];
  1190.     if (t[ti += 6] == ':') {    /* ss if present */
  1191.       db[18] = t[++ti]; db[19] = t[++ti];
  1192.       ti++;            /* move to space */
  1193.     }
  1194.     else db[18] = db[19] = '0';    /* assume 0 seconds */
  1195.                 /* yy -- advance over timezone if necessary */
  1196.     if (++zn == ++ti) ti += (((t[zn] == '+') || (t[zn] == '-')) ? 6 : 4);
  1197.     db[7] = t[ti]; db[8] = t[ti + 1]; db[9] = t[ti + 2]; db[10] = t[ti + 3];
  1198.     t = zn ? (t + zn) : "LCL";    /* zzz */
  1199.     db[21] = *t++; db[22] = *t++; db[23] = *t++;
  1200.     if ((db[21] != '+') && (db[21] != '-')) db[24] = '\0';
  1201.     else {            /* numeric time zone */
  1202.       db[20] = ' '; db[24] = *t++; db[25] = *t++; db[26] = '\0';
  1203.     }
  1204.                 /* set internal date */
  1205.     if (!mail_parse_date (elt,db)) mm_log (datemsg,WARN);
  1206.  
  1207.     curpos += s - tmp;        /* advance position after header */
  1208.     t = strchr (s,'\012');    /* start of next line */
  1209.                 /* find start of next message */
  1210.     while (!(bezrkdos_valid_line (s,NIL,NIL))) {
  1211.       if (t) {            /* have next line? */
  1212.     t++;            /* advance to new line */
  1213.     curpos += t - s;    /* update position and size */
  1214.     elt->rfc822_size += ((t - s) + ((t[-2] == '\015') ? 0 : 1));
  1215.     s = t;            /* move to next line */
  1216.     t = strchr (s,'\012');
  1217.       }
  1218.       else {            /* try next buffer */
  1219.     j = strlen (s);        /* length of unread data in buffer */
  1220.     if ((i = sbuf.st_size - curpos) && (i != j)) {
  1221.                 /* get to that position in the file */
  1222.       lseek (LOCAL->fd,curpos,SEEK_SET);
  1223.                 /* read another buffer's worth */
  1224.       read (LOCAL->fd,s = tmp,j = (int) min (i,(long) MAILTMPLEN));
  1225.       tmp[j] = '\0';    /* tie off buffer */
  1226.       if (!(t = strchr (s,'\012'))) fatal ("Line too long in mailbox");
  1227.     }
  1228.     else {
  1229.       curpos += j;        /* last bit of data */
  1230.       elt->rfc822_size += j;
  1231.       break;
  1232.     }
  1233.       }
  1234.     }
  1235.   }
  1236.                 /* update parsed file size */
  1237.   LOCAL->filesize = sbuf.st_size;
  1238.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1239.   mail_recent (stream,recent);    /* and of change in recent messages */
  1240.   return T;            /* return the winnage */
  1241. }
  1242.  
  1243. /* Bezrkdos copy messages
  1244.  * Accepts: MAIL stream
  1245.  *        mailbox copy vector
  1246.  *        mailbox name
  1247.  * Returns: T if success, NIL if failed
  1248.  */
  1249.  
  1250. long bezrkdos_copy_messages (MAILSTREAM *stream,char *mailbox)
  1251. {
  1252.   char tmp[MAILTMPLEN];
  1253.   struct stat sbuf;
  1254.   MESSAGECACHE *elt;
  1255.   unsigned long i,j,k;
  1256.   int fd;
  1257.                 /* make sure valid mailbox */
  1258.   if (!bezrkdos_isvalid (mailbox) && errno) {
  1259.     if (errno == ENOENT)
  1260.       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
  1261.          (long) NIL);
  1262.     else if (mailboxfile (tmp,mailbox)) {
  1263.       sprintf (tmp,"Not a Bezrkdos-format mailbox: %s",mailbox);
  1264.       mm_log (tmp,ERROR);
  1265.     }
  1266.     else bezrkdos_badname (tmp,mailbox);
  1267.     return NIL;
  1268.   }
  1269.                 /* open the destination */
  1270.   if ((fd = open (mailboxfile (tmp,mailbox),
  1271.           O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
  1272.     sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
  1273.     mm_log (tmp,ERROR);
  1274.     return NIL;
  1275.   }
  1276.   mm_critical (stream);        /* go critical */
  1277.   fstat (fd,&sbuf);        /* get current file size */
  1278.                 /* for each requested message */
  1279.   for (i = 1; i <= stream->nmsgs; i++)
  1280.     if ((elt = mail_elt (stream,i))->sequence) {
  1281.       lseek (LOCAL->fd,elt->data1,SEEK_SET);
  1282.       j = (elt->data2 >> 24) + bezrkdos_size (stream,i);
  1283.       do {            /* read from source position */
  1284.     k = min (j,(unsigned long) MAILTMPLEN);
  1285.     read (LOCAL->fd,tmp,(unsigned int) k);
  1286.     if (write (fd,tmp,(unsigned int) k) < 0) {
  1287.       sprintf (tmp,"Unable to write message: %s",strerror (errno));
  1288.       mm_log (tmp,ERROR);
  1289.       chsize (fd,sbuf.st_size);
  1290.       close (fd);        /* punt */
  1291.       mm_nocritical (stream);
  1292.       return NIL;
  1293.     }
  1294.       } while (j -= k);        /* until done */
  1295.     }
  1296.   close (fd);            /* close the file */
  1297.   mm_nocritical (stream);    /* release critical */
  1298.   return T;
  1299. }
  1300.  
  1301. /* Search support routines
  1302.  * Accepts: MAIL stream
  1303.  *        message number
  1304.  *        pointer to additional data
  1305.  * Returns: T if search matches, else NIL
  1306.  */
  1307.  
  1308.  
  1309. char bezrkdos_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  1310. {
  1311.   return T;            /* ALL always succeeds */
  1312. }
  1313.  
  1314.  
  1315. char bezrkdos_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  1316. {
  1317.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1318. }
  1319.  
  1320.  
  1321. char bezrkdos_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1322. {
  1323.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1324. }
  1325.  
  1326.  
  1327. char bezrkdos_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1328. {
  1329.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1330. }
  1331.  
  1332.  
  1333. char bezrkdos_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1334. {
  1335.   return NIL;
  1336. }
  1337.  
  1338.  
  1339. char bezrkdos_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  1340. {
  1341.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1342.   return (elt->recent && !elt->seen) ? T : NIL;
  1343. }
  1344.  
  1345. char bezrkdos_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  1346. {
  1347.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1348. }
  1349.  
  1350.  
  1351. char bezrkdos_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  1352. {
  1353.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1354. }
  1355.  
  1356.  
  1357. char bezrkdos_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1358. {
  1359.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1360. }
  1361.  
  1362.  
  1363. char bezrkdos_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1364. {
  1365.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1366. }
  1367.  
  1368.  
  1369. char bezrkdos_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1370. {
  1371.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1372. }
  1373.  
  1374.  
  1375. char bezrkdos_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1376. {
  1377.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1378. }
  1379.  
  1380.  
  1381. char bezrkdos_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1382. {
  1383.   return NIL;
  1384. }
  1385.  
  1386.  
  1387. char bezrkdos_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1388. {
  1389.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1390. }
  1391.  
  1392. char bezrkdos_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1393. {
  1394.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1395.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1396. }
  1397.  
  1398.  
  1399. char bezrkdos_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1400. {
  1401.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1402.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1403. }
  1404.  
  1405.  
  1406. char bezrkdos_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1407. {
  1408.                 /* everybody interprets "since" as .GE. */
  1409.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1410.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1411. }
  1412.  
  1413. #define BUFLEN 4*MAILTMPLEN
  1414.  
  1415. char bezrkdos_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1416. {
  1417.   char tmp[BUFLEN];
  1418.   unsigned long bufsize,hdrsize;
  1419.   unsigned long curpos = bezrkdos_header (stream,msgno,&hdrsize);
  1420.   unsigned long textsize = mail_elt (stream,msgno)->rfc822_size - hdrsize;
  1421.                 /* get to header position */
  1422.   lseek (LOCAL->fd,curpos += hdrsize,SEEK_SET);
  1423.   while (textsize) {
  1424.     bufsize = min (textsize,(unsigned long) BUFLEN);
  1425.     read (LOCAL->fd,tmp,(unsigned int) bufsize);
  1426.     if (search (tmp,bufsize,d,n)) return T;
  1427.                 /* backtrack by pattern size if not at end */
  1428.     if (bufsize != textsize) bufsize -= n;
  1429.     textsize -= bufsize;    /* this many bytes handled */
  1430.     curpos += bufsize;        /* advance to that point */
  1431.     lseek (LOCAL->fd,curpos,SEEK_SET);
  1432.   }
  1433.   return NIL;            /* not found */
  1434. }
  1435.  
  1436.  
  1437. char bezrkdos_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1438. {
  1439.   char *s = bezrkdos_fetchstructure (stream,msgno,NIL)->subject;
  1440.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1441. }
  1442.  
  1443.  
  1444. char bezrkdos_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1445. {
  1446.   char tmp[BUFLEN];
  1447.   unsigned long bufsize,hdrsize;
  1448.   unsigned long curpos = bezrkdos_header (stream,msgno,&hdrsize);
  1449.   unsigned long textsize = mail_elt (stream,msgno)->rfc822_size;
  1450.                 /* get to header position */
  1451.   lseek (LOCAL->fd,curpos,SEEK_SET);
  1452.   while (textsize) {
  1453.     bufsize = min (textsize,(unsigned long) BUFLEN);
  1454.     read (LOCAL->fd,tmp,(unsigned int) bufsize);
  1455.     if (search (tmp,bufsize,d,n)) return T;
  1456.                 /* backtrack by pattern size if not at end */
  1457.     if (bufsize != textsize) bufsize -= n;
  1458.     textsize -= bufsize;    /* this many bytes handled */
  1459.     curpos += bufsize;        /* advance to that point */
  1460.     lseek (LOCAL->fd,curpos,SEEK_SET);
  1461.   }
  1462.   return NIL;            /* not found */
  1463. }
  1464.  
  1465. char bezrkdos_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1466. {
  1467.   char tmp[8*MAILTMPLEN];
  1468.   tmp[0] = '\0';        /* initially empty string */
  1469.                 /* get text for address */
  1470.   rfc822_write_address (tmp,bezrkdos_fetchstructure (stream,msgno,NIL)->bcc);
  1471.   return search (tmp,(long) strlen (tmp),d,n);
  1472. }
  1473.  
  1474.  
  1475. char bezrkdos_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1476. {
  1477.   char tmp[8*MAILTMPLEN];
  1478.   tmp[0] = '\0';        /* initially empty string */
  1479.                 /* get text for address */
  1480.   rfc822_write_address (tmp,bezrkdos_fetchstructure (stream,msgno,NIL)->cc);
  1481.   return search (tmp,(long) strlen (tmp),d,n);
  1482. }
  1483.  
  1484.  
  1485. char bezrkdos_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1486. {
  1487.   char tmp[8*MAILTMPLEN];
  1488.   tmp[0] = '\0';        /* initially empty string */
  1489.                 /* get text for address */
  1490.   rfc822_write_address (tmp,bezrkdos_fetchstructure (stream,msgno,NIL)->from);
  1491.   return search (tmp,(long) strlen (tmp),d,n);
  1492. }
  1493.  
  1494.  
  1495. char bezrkdos_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1496. {
  1497.   char tmp[8*MAILTMPLEN];
  1498.   tmp[0] = '\0';        /* initially empty string */
  1499.                 /* get text for address */
  1500.   rfc822_write_address (tmp,bezrkdos_fetchstructure (stream,msgno,NIL)->to);
  1501.   return search (tmp,(long) strlen (tmp),d,n);
  1502. }
  1503.  
  1504. /* Search parsers */
  1505.  
  1506.  
  1507. /* Parse a date
  1508.  * Accepts: function to return
  1509.  *        pointer to date integer to return
  1510.  * Returns: function to return
  1511.  */
  1512.  
  1513. search_t bezrkdos_search_date (search_t f,long *n)
  1514. {
  1515.   long i;
  1516.   char *s;
  1517.   MESSAGECACHE elt;
  1518.                 /* parse the date and return fn if OK */
  1519.   return (bezrkdos_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1520.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1521. }
  1522.  
  1523. /* Parse a flag
  1524.  * Accepts: function to return
  1525.  *        pointer to keyword integer to return
  1526.  *        MAIL stream
  1527.  * Returns: function to return
  1528.  */
  1529.  
  1530. search_t bezrkdos_search_flag (search_t f,long *n,MAILSTREAM *stream)
  1531. {
  1532.   strtok (NIL," ");        /* slurp keyword */
  1533.   return f;
  1534. }
  1535.  
  1536. /* Parse a string
  1537.  * Accepts: function to return
  1538.  *        pointer to string to return
  1539.  *        pointer to string length to return
  1540.  * Returns: function to return
  1541.  */
  1542.  
  1543. search_t bezrkdos_search_string (search_t f,char **d,long *n)
  1544. {
  1545.   char *end = " ";
  1546.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1547.   if (!c) return NIL;        /* missing argument */
  1548.   switch (*c) {            /* see what the argument is */
  1549.   case '{':            /* literal string */
  1550.     *n = strtol (c+1,d,10);    /* get its length */
  1551.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1552.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1553.       char e = *--c;
  1554.       *c = DELIM;        /* make sure not a space */
  1555.       strtok (c," ");        /* reset the strtok mechanism */
  1556.       *c = e;            /* put character back */
  1557.       break;
  1558.     }
  1559.   case '\0':            /* catch bogons */
  1560.   case ' ':
  1561.     return NIL;
  1562.   case '"':            /* quoted string */
  1563.     if (strchr (c+1,'"')) end = "\"";
  1564.     else return NIL;
  1565.   default:            /* atomic string */
  1566.     if (*d = strtok (c,end)) *n = strlen (*d);
  1567.     else return NIL;
  1568.     break;
  1569.   }
  1570.   return f;
  1571. }
  1572.